寬鬆相等(==)允許相等比較中的強制轉型,嚴格相等(===)則不允許強制轉型。
如果比較的是相同型別的值,== 與 === 使用相同的演算法。
若比較的是不同型別的值,應該要確認,是否想要強制轉型。
若想強制轉型,就使用==相等性;若不想,則使用===相等性。
如果被比較的值具有相同的型別,它們就會以同一性(Identity)來比較。42只會等於42,"abc"只會等於"abc"。
少數例外:
比較object(function和array),只有在它們都指向同一個值的參考,才會相等。
使用==相等性來比較,若2邊的型別不同,其中之一(或2者)會進行隱含地強制轉型成相同型別,再進行值的同一性比較。
!=寬鬆不相等,反轉==的比較結果;!==嚴格不相等,反轉===的比較結果。
a === b
會失敗,因為===不允許轉型。
a == b
成功,意味著,它們轉型成相同的型別比較,那究竟是42轉"42",還是"42"轉42呢?
以ES5的規格指出,"42"會被強制轉型成number再進行比較。
若試著將一個值與true/false做比較,有可能出現陷阱。
根據ES5的規格,如果其中一方是boolean的話,那就會強制轉型成number做比較。
Example:
x
是true(boolean),所以會強制轉型成1(number),而"42"轉型成42。1 == 42
的結果很明顯是flase。
y=false
,會變成42 == 0
,結果也是flase。
問題來了,"42" == true
與"42" == false
結果都是false,那"42"究竟是什麼?
實事上,"42"的確是truthy,問題是出在相等性比較式,"42"是truthy值,擁有true的行為,但不會被轉型成true,而true會被轉型成1。切記,"42"再怎麼轉型,永遠都不會是true。
所以"42"是truthy或flasy,跟==運算一點關係都沒有。
不管在任何情況下,都不要使用== true
或== flase
。
但=== true
或=== flase
不會強制轉型,所以不會有這個問題產生。
var a = "42";
//會失敗
if (a == true) {
// ..
}
//會失敗
if (a === true) {
// ..
}
//會成功,隱含轉型
if (a) {
// ..
}
//更好,明確轉型
if (!!a) {
// ..
}
//一樣會成功
if (Boolean( a )) {
// ..
}
null與undefined,以==相等性進行比較,會等於彼此,也等於自身,而且不相等於其他值。
var a = null;
var b;
a == b; // true
a == null; // true
b == null; // true
a == false; // false
b == false; // false
a == ""; // false
b == ""; // false
a == 0; // false
b == 0; // false
null與undefined在==相等性的情況下,彼此的強制轉型是安全的,不會有其他的值導致誤判情形。
善用null與undefined的特性:
var a = doSomething();
if (a == null) {
// ..
}
以上的情形,只有在doSomething()
回傳null或undefined時,if判斷式才會通過,即使是其他的false值也是失敗。
var a = doSomething();
if (a === undefined || a === null) {
// ..
}
使用===不允許強制轉型的話,處理方式就沒那麼漂亮。
array是object,所以[42]會先強制轉型成"42",再轉成42,所以整個運算式會變成42 == 42。
"0" == false; // true
false == 0; // true
false == ""; // true
false == []; // true
"" == 0; // true
"" == []; // true
0 == []; // true
以上的7種情況,都為true,我們來分析這7種情況。
"0" == false
,false => 0,"0" => 0false == 0
,false => 0false == ""
,false => 0," " => 0false == []
,false => 0,[ ] => 0"" == 0
," " => 0"" == []
," " => 0,[ ] => 00 == []
,[ ] => 0
[] == ![]; // true
2 == [2]; // true
"" == [null]; // true
怎麼會是true呢?
[] == ![]
,!
運算子會先將[]
轉型成false,所以實際相等性比較是[] == false
。
2 == [2]
,object會先轉基型值,[2] => "2" => 2。
"" == [null]
,[null] => " " => 0。
請思考 == 相等性,另一邊可能會出現的值。
如果比較的任一邊有 true
、false
、[ ]
、" "
、0
,就不要使用 ==
。
比較項目的圖表,幫助記憶。
來源:https://github.com/dorey/JavaScript-Equality-Table
若遇到a < b
的情況,將2個運算元轉型,若其中一方的結果不是string,那這2個值會強迫轉成number,再進行數值的比較。
var a = [ 42 ];
var b = [ "43" ];
a < b; // true
b < a; // false
若2個值都是string,那就會進行字元的詞典順序比較。
var a = [ "42" ];
var b = [ "043" ];
a < b; // false
"42"會跟"043"逐字元作比較,第一個字元是"4"與"0"的比較,"0"在詞典的順序上小於"4",回傳比較結果是false。
那這個呢?
var a = { b: 42 };
var b = { b: 43 };
a < b; //false
因為a
跟b
都會轉成"[object Object]"
,所以比較上,沒有誰大誰小的問題。
var a = { b: 42 };
var b = { b: 43 };
a < b; // false
a == b; // false
a > b; // false
a <= b; // true
a >= b; // true
比較奇怪的是a == b
,既然都是"[object Object]"
,應該要一樣才對啊,各位不要被剛剛的a < b
範例誤導了。2個物件的比較,比的是「是否為同一個參考」。
依據規格,a <= b
,會先估算b < a
的結果(false),再否定其結果,所以a <= b
會是true。
<=
字面上的意思是「小於或等於」,但更精確的說法應該是「不大於」。所以a <= b
被解讀為!(a > b)
再視為!(b < a)
。!false
等同於true
。
a >= b
先被視為b <= a
,再解讀為!(b > a)
,再一次視為!(a < b)
。!false
等同於true
。
此為You Don't Know JS系列的筆記。